package com.xyzwps.paimon.furina.modules.post

import com.xyzwps.paimon.furina.common.*
import com.xyzwps.paimon.furina.infra.*
import com.xyzwps.paimon.furina.infra.ParameterType.*
import com.xyzwps.paimon.furina.middleware.AuthMW
import org.springdoc.webmvc.core.fn.SpringdocRouteBuilder.route
import org.springframework.stereotype.Component
import org.springframework.web.servlet.function.RouterFunction
import org.springframework.web.servlet.function.ServerRequest
import org.springframework.web.servlet.function.ServerResponse

@Component
class PostApis(
    private val postApp: PostApplication,
    private val collectionApp: PostCollectionApplication,
    private val authMW: AuthMW
) {

    private val optionalAuth: RequestFilter = authMW::optionalAuth
    private val shouldAuth: RequestFilter = authMW::shouldAuth

    companion object {
        const val COLLECTION_TAG = "Post Collections"
        const val POST_TAG = "Post"
    }

    fun collectionRouter(): RouterFunction<ServerResponse> = route()
        .GET("/{id}", hf(optionalAuth, handler = ::getCollectionById)) {
            it.tag(COLLECTION_TAG)
                .operationId("get-a-post-collection")
                .summary("Get a post collection")
                .pathVar("id", required = true, description = "Post collection id", type = INT64)
                .respOK(PostCollectionDTO::class.java)
        }
        .GET("", hf(optionalAuth, handler = ::getCollections)) {
            it.tag(COLLECTION_TAG)
                .operationId("get-all-post-collections")
                .summary("Get all collections")
                .respArray(PostCollectionDTO::class.java)
        }
        .POST("", acceptJson, hf(shouldAuth, handler = ::createCollection)) {
            it.tag(COLLECTION_TAG)
                .operationId("create-a-post-collection")
                .summary("Create a collection")
                .desc(shouldAuth = true)
                .reqBody(PostCollectionSavePayload::class.java)
                .respOK(PostCollectionDTO::class.java)
        }
        .PATCH("/{id}", acceptJson, hf(shouldAuth, handler = ::updateCollection)) {
            it.tag(COLLECTION_TAG)
                .operationId("update-post-collection")
                .summary("Update a collection")
                .desc(shouldAuth = true)
                .pathVar("id", required = true, description = "Post collection id", type = INT64)
                .reqBody(PostCollectionSavePayload::class.java)
                .respOK(PostCollectionDTO::class.java)
        }
        .DELETE("/{id}", hf(shouldAuth, handler = ::deleteCollection)) {
            it.tag(COLLECTION_TAG)
                .operationId("delete-post-collection")
                .summary("Delete post collection")
                .desc(shouldAuth = true)
                .pathVar("id", required = true, description = "Post collection id", type = INT64)
                .respOK(OK::class.java)
        }
        .build()

    fun createCollection(req: ServerRequest): JsonResponse {
        val payload = req.validatedBody<PostCollectionSavePayload>()
        return JsonResponse().body(collectionApp.create(req.userId, payload))
    }

    fun updateCollection(req: ServerRequest): JsonResponse {
        val id = req.pathVariableLong("id")
        val payload = req.validatedBody<PostCollectionSavePayload>()
        return JsonResponse().body(collectionApp.update(id, req.userId, payload))
    }

    fun deleteCollection(req: ServerRequest): JsonResponse {
        val id = req.pathVariableLong("id")
        collectionApp.delete(id, req.userId)
        return JsonResponse().body(OK)
    }

    fun getCollectionById(req: ServerRequest): JsonResponse {
        val id = req.pathVariableLong("id")
        return JsonResponse().body(collectionApp.getCollectionById(id, req.userId))
    }

    fun getCollections(req: ServerRequest): JsonResponse {
        return JsonResponse().body(collectionApp.getCollections(req.userId))
    }

    fun postRouter(): RouterFunction<ServerResponse> = route()
        .GET("/{id}", hf(shouldAuth, handler = ::getPostById)) {
            it.tag(POST_TAG)
                .operationId("get-a-post")
                .summary("Get a post")
                .desc(shouldAuth = true)
                .pathVar("id", required = true, description = "Post id", type = INT64)
                .respOK(PostDTO::class.java)
        }
        .GET("", hf(shouldAuth, handler = ::getPosts)) {
            it.tag(POST_TAG)
                .operationId("get-posts")
                .summary("Get posts")
                .desc(shouldAuth = true)
                .query("collectionId", description = "Post collection id", type = INT64)
                .respArray(PostDTO::class.java)
        }
        .POST("", acceptJson, hf(shouldAuth, handler = ::createPost)) {
            it.tag(POST_TAG)
                .operationId("create-a-post")
                .summary("Create a post")
                .desc(shouldAuth = true)
                .reqBody(PostCreatePayload::class.java)
                .respOK(PostDTO::class.java)
        }
        .PATCH("/{id}", acceptJson, hf(shouldAuth, handler = ::updatePost)) {
            it.tag(POST_TAG)
                .operationId("update-a-post")
                .summary("Update post")
                .desc(shouldAuth = true)
                .pathVar("id", required = true, description = "Post id", type = INT64)
                .reqBody(PostUpdatePayload::class.java)
                .respOK(PostDTO::class.java)
        }
        .DELETE("/{id}", hf(shouldAuth, handler = ::deletePost)) {
            it.tag(POST_TAG)
                .operationId("delete-post")
                .summary("Delete post")
                .desc(shouldAuth = true)
                .pathVar("id", required = true, description = "Post id", type = INT64)
                .respOK(OK::class.java)
        }
        .build()

    fun createPost(req: ServerRequest): JsonResponse {
        val userId = req.userId
        val payload = req.validatedBody<PostCreatePayload>()
        return JsonResponse().body(postApp.create(userId, payload))
    }

    fun updatePost(req: ServerRequest): JsonResponse {
        val id = req.pathVariableLong("id")
        val userId = req.userId
        val payload = req.validatedBody<PostUpdatePayload>()
        return JsonResponse().body(postApp.update(id, userId, payload))
    }

    fun deletePost(req: ServerRequest): JsonResponse {
        val id = req.pathVariableLong("id")
        val userId = req.userId
        postApp.deleteById(id, userId)
        return JsonResponse().body(OK)
    }

    fun getPostById(req: ServerRequest): JsonResponse {
        val id = req.pathVariableLong("id")
        return if (req.authenticated) {
            JsonResponse().body(postApp.findById(id, req.userId))
        } else {
            JsonResponse().body(postApp.findById(id))
        }
    }

    fun getPosts(req: ServerRequest): JsonResponse {
        val collectionId = req.paramLong("collectionId")
            ?: throw AppException(AppErrorCode.SEARCH_PARAM_REQUIRED, "Search param collectionId is required.")
        return if (req.authenticated) {
            JsonResponse().body(postApp.findByCollectionId(collectionId, req.userId))
        } else {
            JsonResponse().body(postApp.findByCollectionId(collectionId))
        }
    }
}